/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.service.evaluation;

import cn.ac.nercis.pes.common.event.ProcessCallback;
import cn.ac.nercis.pes.common.constant.excel.AssetExcelSheet;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.common.ResultCode;
import cn.ac.nercis.pes.repository.dal.project.SystemRepository;
import cn.ac.nercis.pes.repository.mapper.evaluation.AssetImportMapper;
import cn.ac.nercis.pes.repository.mapper.evaluation.PersonnelMapper;
import cn.ac.nercis.pes.repository.mapper.evaluation.SecurityDocumentMapper;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.Objects;

/**
 * 系统资产导出服务类
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class AssetExportService {
    private final static String EXPORT_FILE_NAME_FORMAT = "%s资产文档.xlsx";

    private final AssetService assetService;
    private final SystemRepository systemRepository;

    private final SecurityDocumentService securityDocumentService;

    private final PersonnelService personnelService;

    private ProcessCallback processCallback;

    @Autowired
    public AssetExportService(AssetService assetService,
                              SystemRepository systemRepository,
                              SecurityDocumentService securityDocumentService,
                              PersonnelService personnelService) {
        this.assetService = assetService;
        this.systemRepository = systemRepository;
        this.securityDocumentService = securityDocumentService;
        this.personnelService = personnelService;
    }

    /**
     * 设置接收进度提示的回调方法
     * @param callback 回调方法接口实现
     */
    public void setProcessCallback(ProcessCallback callback){
        this.processCallback = callback;
    }

    /**
     * 导出系统资产
     * @param systemId 系统Id
     * @param filePath 系统资产存储路径
     * @return 资产导出结果
     */
    public Result<String> exportAsset(@NonNull String systemId, File filePath, String templateFile){
        SystemDO systemDO = systemRepository.findById(systemId).orElse(null);
        if(Objects.isNull(systemDO)){
            return Result.failed(ResultCode.SYSTEM_NOT_EXIST);
        }
        var exportFilePath = FileUtils.getFile(filePath,String.format(EXPORT_FILE_NAME_FORMAT,systemDO.getName()));
        try(var inputStream = FileUtils.openInputStream(FileUtils.getFile(templateFile));
            var excelWriter = EasyExcel.write(exportFilePath)
                .withTemplate(inputStream)
                .build()) {
            exportPhyEnvAssets(excelWriter, systemId);
            exportPhySecurityAssets(excelWriter, systemId);
            exportPwdProductAssets(excelWriter, systemId);
            exportServerStorageAssets(excelWriter, systemId);
            exportNetworkSecurityAssets(excelWriter, systemId);
            exportDatabaseAssets(excelWriter, systemId);
            exportKeyBusinessAssets(excelWriter, systemId);
            exportSubBusinessAssets(excelWriter, systemId);
            exportKeyDataAssets(excelWriter, systemId);
            exportSecurityDocAssets(excelWriter, systemId);
            exportPersonnelAssets(excelWriter, systemId);
            processNotify();
            excelWriter.finish();
            return Result.success(exportFilePath.getAbsolutePath());
        }catch (Exception ex){
            log.error("导出系统资产出错！",ex);
        }
        return Result.failed("");
    }

    private void exportPhyEnvAssets(ExcelWriter excelWriter,String systemId){
        processNotify(0.1,String.format("正在导出%s资产数据...", AssetExcelSheet.PHY_ENV.getName()));
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.PHY_ENV.getName()).build();
        var assetData = assetService.findPhyEnv(StringUtils.EMPTY,systemId)
                .stream()
                .map(AssetImportMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData,writeSheet);
    }

    private void exportPhySecurityAssets(ExcelWriter excelWriter, String systemId) {
        processNotify(0.2, String.format("正在导出%s资产数据...", AssetExcelSheet.PHY_SECURITY.getName()));
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.PHY_SECURITY.getName()).build();
        var assetData = assetService.findPhySecurity(StringUtils.EMPTY, systemId)
                .stream()
                .map(AssetImportMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData, writeSheet);
    }

    private void exportPwdProductAssets(ExcelWriter excelWriter, String systemId) {
        processNotify(0.3, String.format("正在导出%s资产数据...", AssetExcelSheet.PWD_PRODUCT.getName()));
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.PWD_PRODUCT.getName()).build();
        var assetData = assetService.findPwdProduct(StringUtils.EMPTY, systemId)
                .stream()
                .map(AssetImportMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData, writeSheet);
    }

    private void exportServerStorageAssets(ExcelWriter excelWriter, String systemId) {
        processNotify(0.4, String.format("正在导出%s资产数据...", AssetExcelSheet.SERVER_STORAGE.getName()));
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.SERVER_STORAGE.getName()).build();
        var assetData = assetService.findServerStorage(StringUtils.EMPTY, systemId)
                .stream()
                .map(AssetImportMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData, writeSheet);
    }

    private void exportNetworkSecurityAssets(ExcelWriter excelWriter, String systemId) {
        processNotify(0.5, String.format("正在导出%s资产数据...", AssetExcelSheet.NETWORK_SECURITY.getName()));
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.NETWORK_SECURITY.getName()).build();
        var assetData = assetService.findNetworkSecurity(StringUtils.EMPTY, systemId)
                .stream()
                .map(AssetImportMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData, writeSheet);
    }

    private void exportDatabaseAssets(ExcelWriter excelWriter, String systemId) {
        processNotify(0.6, String.format("正在导出%s资产数据...", AssetExcelSheet.DATABASE.getName()));
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.DATABASE.getName()).build();
        var assetData = assetService.findDatabase(StringUtils.EMPTY, systemId)
                .stream()
                .map(AssetImportMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData, writeSheet);
    }

    private void exportKeyBusinessAssets(ExcelWriter excelWriter, String systemId) {
        processNotify(0.7, String.format("正在导出%s资产数据...", AssetExcelSheet.KEY_BUSINESS.getName()));
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.KEY_BUSINESS.getName()).build();
        var assetData = assetService.findKeyBusiness(StringUtils.EMPTY, systemId)
                .stream()
                .map(AssetImportMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData, writeSheet);
    }

    private void exportSubBusinessAssets(ExcelWriter excelWriter, String systemId) {
        processNotify(0.8, String.format("正在导出%s资产数据...", AssetExcelSheet.SUB_BUSINESS.getName()));
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.SUB_BUSINESS.getName()).build();
        var assetData = assetService.findSubBusiness(systemId, StringUtils.EMPTY)
                .stream()
                .map(AssetImportMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData, writeSheet);
    }

    private void exportKeyDataAssets(ExcelWriter excelWriter, String systemId) {
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.KEY_DATA.getName()).build();
        var assetData = assetService.findKeyData(StringUtils.EMPTY, systemId)
                .stream()
                .map(AssetImportMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData, writeSheet);
    }

    private void exportSecurityDocAssets(ExcelWriter excelWriter, String systemId) {
        processNotify(0.9, String.format("正在导出%s资产数据...", AssetExcelSheet.SECURITY_DOC.getName()));
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.SECURITY_DOC.getName()).build();
        var assetData = securityDocumentService.findSecurityDocument(systemId)
                .stream()
                .map(SecurityDocumentMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData, writeSheet);
    }

    private void exportPersonnelAssets(ExcelWriter excelWriter, String systemId) {
        var writeSheet = EasyExcel.writerSheet(AssetExcelSheet.PERSONNEL.getName()).build();
        var assetData = personnelService.findPersonnel(systemId)
                .stream()
                .map(PersonnelMapper.INSTANCE::toBO)
                .toList();
        excelWriter.write(assetData, writeSheet);
    }

    private void processNotify(double value, String tip) {
        if (Objects.nonNull(processCallback)) {
            processCallback.process(value, tip);
        }
    }

    private void processNotify() {
        if (Objects.nonNull(processCallback)) {
            processCallback.process(1, "");
        }
    }
}